﻿using System.Collections.Generic;
using Commons.Models.OperationsContext;
using Commons.Models.TradeValues;
using Commons.Services.Locks;

namespace Commons.Services.TradeValues
{
	public class MultipleTradeValueProcessor : ITradeValueProcessor
	{
		public ICostProcessor CostProcessor { protected get; set; }
		public IRewardProcessor RewardProcessor { protected get; set; }
		public ILockService LockService { protected get; set; }

		public IEnumerable<CostStatus> GetCostsStatus(ITradeValue value, IOperationContext context, int quantity)
		{
			IEnumerable<ITradeValue> tradeValues = PreProcessCost(value, context);
			foreach (ITradeValue tradeValue in tradeValues)
			{
				IEnumerable<CostStatus> costs = CostProcessor.GetCostsStatus(tradeValue, context, quantity);
				foreach (CostStatus cost in costs)
				{
					yield return cost;
				}
			}
		}


		public IEnumerable<ITradeValue> PreProcessCost(ITradeValue value, IOperationContext context)
		{
			MultipleTradeValue multipleTradeValue = value as MultipleTradeValue;

			Dictionary<string, ITradeValue> mergeTVMap = new Dictionary<string, ITradeValue>();

			foreach (ITradeValue tradeValue in multipleTradeValue.Values)
			{
				IEnumerable<ITradeValue> preProcessValues = CostProcessor.PreProcessCost(tradeValue, context);

				foreach (ITradeValue preprocessTv in preProcessValues)
				{
					if (LockService.CheckLock(preprocessTv.Lock))
					{
						if (preprocessTv is IMergeableTradeValue)
						{
							string key = ((IMergeableTradeValue) preprocessTv).GetKey();
							
							ITradeValue mergeTV;
							if (mergeTVMap.TryGetValue(key, out mergeTV))
								mergeTVMap[key] = ((IMergeableTradeValue) mergeTV).Merge(preprocessTv);
							else
								mergeTVMap.Add(key, preprocessTv);
						}
						else
						{
							yield return preprocessTv;
						}
					}
				}
			}

			if (mergeTVMap.Count > 0)
			{
				foreach (ITradeValue tv in mergeTVMap.Values)
					yield return tv;
			}
		}

		public IEnumerable<ITradeValue> ProcessCost(ITradeValue value, IOperationContext context, int quantity)
		{
			List<ITradeValue> values = new List<ITradeValue>();

			if (LockService.CheckLock(value.Lock)) 
			{
				IEnumerable<ITradeValue> tradeValues = PreProcessCost(value, context);
				foreach (ITradeValue tradeValue in tradeValues)
				{
					values.AddRange(CostProcessor.ProcessCost(tradeValue, context, quantity));
				}
			}

			return values;
		}

		public IEnumerable<ITradeValue> PreProcessReward(ITradeValue value, IOperationContext context)
		{
			MultipleTradeValue multipleTradeValue = value as MultipleTradeValue;

			foreach (ITradeValue tradeValue in multipleTradeValue.Values)
			{
				IEnumerable<ITradeValue> values = RewardProcessor.PreProcessReward(tradeValue, context);
				foreach (ITradeValue rewardValue in values)
				{
					if (LockService.CheckLock(rewardValue.Lock))
						yield return rewardValue;
				}
			}
		}

		public IEnumerable<ITradeValue> ProcessReward(ITradeValue value, IOperationContext context, int quantity)
		{
			List<ITradeValue> values = new List<ITradeValue>();

			if (LockService.CheckLock(value.Lock))
			{
				IEnumerable<ITradeValue> tradeValues = PreProcessReward(value, context);
				foreach (ITradeValue tradeValue in tradeValues)
				{
					values.AddRange(RewardProcessor.ProcessReward(tradeValue, context, quantity));
				}
			}

			return values;
		}
	}
}
